#pragma once

#include "fxcc/core/graph/common/pch.h"
#include "fxcc/core/platform/common/Input.h"

namespace fxcc
{
	namespace graph
	{
		namespace common
		{

			struct FXCC_API    Transform :public Base
			{

				glm::vec3 m_Position{0.0f};
				
				glm::vec3 m_Rotation{0.0f};
				
				glm::vec3 m_Scale{1.0f};

				Transform();

				glm::vec3 GetRight() const;

				glm::vec3 GetRightRH() const;
				
				glm::vec3 GetRightLH() const;

				glm::vec3 GetForward() const;
				
				glm::vec3 GetUp() const;

				glm::mat4 GetViewMatrix() const;

				glm::mat4 GetViewMatrixLH() const;

				glm::mat4 GetViewMatriRH() const;

				glm::mat4 GetViewMatrixNoTranslation() const;

				glm::mat4 GetWorld() const;

				glm::mat4 GetPosRotMatrix() const;

				Transform(const glm::vec3& pos, const glm::quat& rot, const glm::vec3& scale);

				void CreateFrom(const glm::mat4& local);
				
				void RotateX(float d);
				
				void RotateY(float d);
				
				void RotateZ(float d);
				
				void Rotate(glm::vec3);

				void Decompose(const glm::mat4& source);

				void Fixed();

				void SetPosition(const glm::vec3& pos);

				void SetRotation(const glm::quat& q);
				
				void SetScale(const glm::vec3& scale);

				void Scale(float d);

				// Opengl RH
				void BackAt(glm::vec3 pos, glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f));
				void LookAt(glm::vec3 pos, glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f));

				// DirectX LH
				void BackAtLH(glm::vec3 pos, glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f));
				void LookAtLH(glm::vec3 pos, glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f));

				glm::mat4 GetRotationMatrix() const;
				glm::quat GetRotationQuat() const;

				void Move(float dist, glm::vec3 f);
				void Translate(float dist);
				void MoveRight(float dist);

				void Scale(glm::vec3 o, float d);
				glm::vec3 GetPosXZ() const;
				glm::vec3 GetPosYZ() const;
				glm::vec3 GetPosXY() const;

				float GetToXZSq();

				static glm::vec3 Xup();
				static glm::vec3 Zup();
				static glm::vec3 Yup();
				static Transform Slerp(const Transform& t1, const Transform& t2, float axis);
				static glm::vec3 GetEulerAnglesFromRotationMatrix(const glm::mat4& rotationMatrix);

				fxcc::graph::common::Transform operator*(const fxcc::graph::common::Transform &other) const
				{
					glm::mat4 resMat4 = GetWorld() * other.GetWorld();
					return fxcc::graph::common::Transform(resMat4);
				}
				Transform(const glm::mat4 source)
				{
					CreateFrom(source);
				}

			public:
				void FreeMove(const fxcc::platform::common::Input& input,
					const float& deltaTime, 
					const glm::vec2& sensitivity_xy = { 20.f, 20.f },
					const glm::vec2& trun_sensitivity_xy = { 20.f,20.f }
					);
			};
			inline void Load(const aiMatrix4x4 &source, fxcc::graph::common::Transform &target)
			{
				glm::mat4 m1;
				fxcc::graph::common::Load(source, m1);
				target.CreateFrom(m1);
			}
		};
	};
};